package com.gitee.l0km.xthrift.thrift.protocol;

import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolDecorator;
import org.apache.thrift.transport.AutoExpandingBufferWriteTransport;
import org.apache.thrift.transport.TMemoryInputTransport;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;

import com.google.common.base.Throwables;

import java.lang.reflect.Field;
import java.util.Arrays;

/**
 * 基于{@link TProtocolDecorator}实现{@link TTransport}可重复读取
 * 
 * @author guyadong
 * @since 1.3.0
 *
 */
public class BufferedProtocol extends TProtocolDecorator {
	/**
	 * 装饰实例
	 * */
	public final TProtocol delegate;
	/**
	 * 从装饰{@link #delegate}中读取的数据缓冲区
	 */
	private final byte[] buffer;

	private BufferedProtocol(TProtocol protocol) {
		this(protocol,null);
	}
	private BufferedProtocol(TProtocol protocol,byte[] buffer) {
		super(protocol);
		this.delegate = protocol;
		try {
			/**
			 * 读取输入的{@link TTransport}所有数据创建{@link TMemoryInputTransport}实例
			 */
			this.buffer = null == buffer ? copy(protocol.getTransport()) : buffer;
			this.trans_ = new TMemoryInputTransport(this.buffer);
			/** 等价于 this.delegate.trans_ = this.trans_; */
			setTransport(this.delegate, this.trans_);
		} catch (TTransportException e) {
			throw new RuntimeException(e);
		}
	}
	private static byte[] copy(TTransport transport) throws TTransportException {
		try (AutoExpandingBufferWriteTransport autoExpandingTransport = new AutoExpandingBufferWriteTransport(1024,
				1.5)) {
			int readCount;
			byte[] buffer = new byte[1024];
			while ((readCount = transport.read(buffer, 0, buffer.length)) > 0) {
				autoExpandingTransport.write(buffer, 0, readCount);
			}
			return Arrays.copyOfRange(autoExpandingTransport.getBuf().array(), 0, autoExpandingTransport.getPos());
		}
	}
	/**
	 * 复位 TTransport, 以支持从 {@link #buffer} 重复读取
	 */
	@Override
	public void reset() {
		delegate.reset();
		((TMemoryInputTransport)delegate.getTransport()).reset(buffer);
	}
	public static BufferedProtocol of(TProtocol protocol) {
		return new BufferedProtocol(protocol);
	}
	/**
	 * 反射修改{@link TProtocol}的'trans_'字段
	 */
	private static void setTransport(TProtocol protocol, TTransport transport) {
		try {
			Field field = TProtocol.class.getDeclaredField("trans_");
			field.setAccessible(true);
			field.set(protocol, transport);
		} catch (Exception e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
	}
}
